package com.changgou.search.service.impl;


import com.alibaba.fastjson.JSON;
import com.changgou.goods.fegin.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.search.service.SkuService;
import entity.Result;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import search.pojo.SkuInfo;

import java.util.*;

@Service
public class SkuServiceBerFen implements SkuService {
    @Autowired
    private SkuFeign skuFeign;
    @Autowired
    private SkuEsMapper skuEsMapper;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 导入es索引库
     */
    @Override
    public void importDate() {
        Result<List<Sku>> skuResult = skuFeign.findByStatus("1");
        List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(skuResult.getData()), SkuInfo.class);
        /*添加转换规格信息*/
        for (SkuInfo skuInfo : skuInfos) {
            Map<String, Object> specMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
            skuInfo.setSpecMap(specMap);
        }
        skuEsMapper.saveAll(skuInfos);
    }

    /**
     * 搜索业务
     *
     * @param searchMap
     * @return
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {
        /*搜索条件封装*/
        NativeSearchQueryBuilder nativeSearchQueryBuilder = buildBasicQuery(searchMap);
        /*数据结果集搜索*/
        HashMap<String, Object> resultMap = searchList(nativeSearchQueryBuilder);

        if (searchMap == null && StringUtils.isEmpty(searchMap.get("category"))) {
            /*分类分组查询*/
            List<String> categoryList = searchCategoryList(nativeSearchQueryBuilder);
            resultMap.put("categoryList", categoryList);
        }
        if (searchMap == null && StringUtils.isEmpty(searchMap.get("brand"))) {
            /*品牌集合查询*/
            List<String> brandList = searchBrandList(nativeSearchQueryBuilder);
            resultMap.put("brandList", brandList);
        }

        /*规格查询*/
        HashMap<String, Set<String>> specList = searchSpecList(nativeSearchQueryBuilder);
        resultMap.put("specList", specList);
        return resultMap;

    }


    /**
     * 搜索条件封装
     *
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder buildBasicQuery(Map<String, String> searchMap) {
        /*搜索条件构件对象，用于封装各种搜索条件*/
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        /*根据关键词搜索*/
        if (searchMap != null && searchMap.size() > 0) {
            String keywords = searchMap.get("keywords");
            if (!StringUtils.isEmpty(keywords)) {
                // 根据关键词 搜索ES中的name域
                boolQueryBuilder.must(QueryBuilders.queryStringQuery(keywords).field("name"));
            }
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                boolQueryBuilder.must(QueryBuilders.termQuery("categoryName", searchMap.get("category")));
            }
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                boolQueryBuilder.must(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
            }
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                String key = entry.getKey();
                // 如果key 以spec_开始 ，则表示规格筛选查询
                if (key.startsWith("spec_")) {
                    String value = entry.getValue();
                    //spec_网络 spec_前五个去掉
                    boolQueryBuilder.must(QueryBuilders.termQuery("specMap" + key.substring(5) + ".keyword", value));
                }
            }
            String price = searchMap.get("price");
            if (!StringUtils.isEmpty(price)) {
                //0-500 元 500-1000元  3000以上
                price = price.replace("元", "").replace("以上", "");
                String[] prices = price.split("-");
                if (prices != null && prices.length > 0) {
                    /**
                     * rangeQuery 范围搜索
                     * gt 大于
                     * lte 小于等于
                     */
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0])));
                    if (prices.length == 2) {
                        boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[0])));
                    }

                }

            }
            /*排序实现*/
            String sortField = searchMap.get("sortField"); // 排序的域
            String sortRule = searchMap.get("sortRule"); // 排序的规则 升序 降序
            if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)) {
                FieldSortBuilder fieldSortBuilder = new FieldSortBuilder(sortField).order(SortOrder.valueOf(sortRule));
                nativeSearchQueryBuilder.withSort(fieldSortBuilder);
            }

        }

        // 分页,如果不传分页参数 默认第一页
        Integer pageNum = converterPage(searchMap);
        Integer pageSize = 30; // 每页显示的记录
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        return nativeSearchQueryBuilder;
    }

    /**
     * 接受前段传入得分页参数
     *
     * @return
     */
    public Integer converterPage(Map<String, String> searchMap) {
        if (searchMap != null) {
            String pageNum = searchMap.get("pageNum");
            try {
                int i = Integer.parseInt(pageNum);
                if (i < 0) {
                    return 1;
                }
                return i;
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        return 1;

    }

    /**
     * 数据结果集搜索
     *
     * @param nativeSearchQueryBuilder
     * @return
     */
    private HashMap<String, Object> searchList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {

        /*添加高亮*/
        HighlightBuilder.Field field = new HighlightBuilder.Field("name"); // 指定高亮域
        nativeSearchQueryBuilder.withHighlightFields(field);
        // 前缀<em style = "color:red"> // 后缀</em>
        field.preTags("<em style = \"color:red;\">");
        field.postTags("</em>");
        // 碎片长度 关键词数据传长度
        field.fragmentSize(100);
        nativeSearchQueryBuilder.withHighlightFields(field);

//        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(
                nativeSearchQueryBuilder.build(), // 搜托条件封装
                SkuInfo.class, //数据集药转换为的类型
                new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                        List<T> list = new ArrayList<>();  // 存储所有的高亮数据
                        //执行查询，获取所有结果集【高亮和非高亮 】
                        SearchHits hits = response.getHits(); // 获取所有数据
                        for (SearchHit hit : hits) {
                            // 分析结果集  获取非高亮数据
                            SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                            //分析结果数据集，获取高亮数据
                            HighlightField name = hit.getHighlightFields().get("name");
                            if (name!=null&&name.getFragments()!=null){
                                // 高亮数据读取
                                Text[] fragments = name.getFragments();
                                StringBuffer buffer = new StringBuffer();
                                for (Text fragment : fragments) {
                                    buffer.append(fragment.string());
                                }
                                // 非高亮 替换高亮
                                skuInfo.setName(buffer.toString());
                            }
                            list.add((T) skuInfo);
                        }
                        /**
                         * 1 搜索的集合数据，携带高亮：List<T> content
                         * 2 分页对象
                         * 3 总条数
                         */
                        return  new AggregatedPageImpl<T>(list,pageable,response.getHits().getTotalHits());
                    }
                });  // 执行搜索后，将数据结果集封装到这个里面

        /*获取结果集*/
        List<SkuInfo> contents = page.getContent();
        /*总记录数*/
        long totalElements = page.getTotalElements();
        /*总页数*/
        int totalPages = page.getTotalPages();
        /*封装一个map封装返回数据*/
        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("rows", contents);
        resultMap.put("total", totalElements);
        resultMap.put("totalPages", totalPages);
        return resultMap;
    }

    /**
     * 分类分组查询
     *
     * @param nativeSearchQueryBuilder
     * @return
     */
    private List<String> searchCategoryList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {
        /**
         * 分组查询分类集合
         * addAggregation 添加聚合操作
         * .terms 取别名
         * .field 根据那个域查询
         */
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
        /*获取聚合数据集合  StringTerms : 把接收到的数据转换成字符串对象*/
        StringTerms skuCategory = aggregatedPage.getAggregations().get("skuCategory");
        List<String> categoryList = new ArrayList<>();
        for (StringTerms.Bucket bucket : skuCategory.getBuckets()) {
            categoryList.add(bucket.getKeyAsString());
        }
        return categoryList;
    }


    /**
     * 品牌分组查询
     *
     * @param nativeSearchQueryBuilder
     * @return
     */
    private List<String> searchBrandList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {
        /**
         * 分组查询品牌集合
         * addAggregation 添加聚合操作
         * .terms 取别名
         * .field 根据那个域查询
         */
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
        /*获取聚合数据集合  StringTerms : 把接收到的数据转换成字符串对象*/
        StringTerms skuBrand = aggregatedPage.getAggregations().get("skuBrand");
        List<String> brandList = new ArrayList<>();
        for (StringTerms.Bucket bucket : skuBrand.getBuckets()) {
            brandList.add(bucket.getKeyAsString());
        }
        return brandList;
    }


    /**
     * 规格查询
     *
     * @param nativeSearchQueryBuilder
     * @return
     */
    private HashMap<String, Set<String>> searchSpecList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {
        /**
         * 规格查询 spec.keyword : spec域不分词
         * addAggregation 添加聚合操作
         * .terms 取别名
         * .field 根据那个域查询
         * .size(10000) 每页显示多少条 默认不写 是10条
         */
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(10000));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);

        /*获取聚合数据集合  StringTerms : 把接收到的数据转换成字符串对象*/
        StringTerms skuBrand = aggregatedPage.getAggregations().get("skuSpec");
        List<String> specList = new ArrayList<>();
        for (StringTerms.Bucket bucket : skuBrand.getBuckets()) {
            String specName = bucket.getKeyAsString();
            specList.add(specName);
        }
        return putAllSpec(specList);
    }

    /**
     * 规格汇总合并
     *
     * @param specList
     * @return
     */
    private HashMap<String, Set<String>> putAllSpec(List<String> specList) {
        //合并后的map对象
        HashMap<String, Set<String>> allSpec = new HashMap<>();
        //  规格名 ： 规格1 规格2 规格3
        Map<String, Set<String>> map = new HashMap<>();
        for (String spec : specList) {
            Map<String, String> specMap = JSON.parseObject(spec, Map.class);
            for (Map.Entry<String, String> entry : specMap.entrySet()) {
                String key = entry.getKey(); //规格名字
                String value = entry.getValue(); // 规格值
                Set<String> specSet = map.get(key);
                if (specSet == null) {
                    specSet = new HashSet<>();
                }
                specSet.add(value);
                allSpec.put(key, specSet);
            }
        }
        return allSpec;
    }

}
